#ifndef GAMEENGINE_GEOMETRY_CORE_INTERVAL_HPP
#define GAMEENGINE_GEOMETRY_CORE_INTERVAL_HPP

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================
// Interval class

//! Default constructor
/*!
 @note Class T should have a default constructor
 @note The interval is defaulted to a full interval, i.e., ]-infinity, +infinity[
 */
template <class T>
Interval<T>::Interval() : min_defined_(false), max_defined_(false), min_included_(true), max_included_(true) {}

//! Constructor from a minimum and a maximum value
/*!
 @param min_defined Lower bound of the interval
 @param max_defined Higher bound of the interval
 @note Class T should have a copy constructor
 */
template <class T>
Interval<T>::Interval(const T& min_defined, const T& max_defined) :
	min_(min_defined), max_(max_defined), min_defined_(true), max_defined_(true), min_included_(true), max_included_(true) {}

//! Copy constructor
/*!
 @param rhs The interval to copy
 @note Class T should have a copy constructor
 */
template <class T>
Interval<T>::Interval(const Interval& rhs) :
	min_(rhs.min_), max_(rhs.max_),
	min_defined_(rhs.min_defined_), max_defined_(rhs.max_defined_),
	min_included_(rhs.min_included_), max_included_(rhs.max_included_) {}

//! Destructor
template <class T>
Interval<T>::~Interval() {}

//! Assignment operator
/*!
 @param rhs The interval to copy
 @note Class T should have an assignment operator
 */
template <class T>
Interval<T>& Interval<T>::operator=(const Interval& rhs) {
	if ( &rhs != this ) {
		min_ = rhs.min_;
		max_ = rhs.max_;
		min_defined_ = rhs.min_defined_;
		max_defined_ = rhs.max_defined_;
		min_included_ = rhs.min_included_;
		max_included_ = rhs.max_included_;
	}
	return *this;
}

//! Comparison operator
/*!
 @param rhs The interval to compare
 @return True if the two intervals are exactly the same, false otherwise
 @note Class T should have a difference operator
 @note The values of the lower and higher bounds are compared even if
       those bounds are not used (because they are set to +/- infinity)
 */
template <class T>
bool Interval<T>::operator==(const Interval& rhs) const {
	// Compare bounds
	if ( min_ != rhs.min_ ) return false;
	if ( max_ != rhs.max_ ) return false;

	// Compare infinite bounds
	if ( min_defined_ != rhs.min_defined_ ) return false;
	if ( max_defined_ != rhs.max_defined_ ) return false;

	// Compare inclusion rules
	if ( min_included_ != rhs.min_included_ ) return false;
	if ( max_included_ != rhs.max_included_ ) return false;

	// If no difference was observed, the intervals are the same
	return true;
}

//! Difference operator
/*!
 @param rhs The interval to compare
 @return True if the two intervals are different, false otherwise
 @note Class T should have a difference operator
 @note The values of the lower and higher bounds are compared even if
       those bounds are not used (because they are set to +/- infinity)
 */
template <class T>
bool Interval<T>::operator!=(const Interval& rhs) const {
	return ( operator==(rhs) == false );
}

//! Get the lower bound of the interval, if not equal to -infinity
/*!
 @param use_min If not nil, the use_min value receives true if the lower
                bound is defined, false otherwise (i.e., if the lower bound
				is equal to -infinity)
 @return The lower bound of the interval. Note that this value is incorrect
         if the interval has no lower bound.
 */
template <class T>
T Interval<T>::get_min(bool* use_min) const {
	if ( use_min ) *use_min = min_defined_;
	return min_;
}

//! Get the higher bound of the interval, if not equal to +infinity
/*!
 @param use_max If not nil, the use_max value receives true if the higher
                bound is defined, false otherwise (i.e., if the higher bound
				is equal to +infinity)
 @return The higher bound of the interval. Note that this value is incorrect
         if the interval has no higher bound.
 */
template <class T>
T Interval<T>::get_max(bool* use_max) const {
	if ( use_max ) *use_max = max_defined_;
	return max_;
}

//! Set the lower bound of the interval
/*!
 @param min The new lower bound of the interval
 @note If the lower bound is larger than the higher bound, the interval is empty
 */
template <class T>
void Interval<T>::set_min(const T& min) {
	min_defined_ = true;
	min_ = min;
}

//! Set the higher bound of the interval
/*!
 @param max The new higher bound of the interval
 @note If the higher bound is below the lower bound, the interval is empty
 */
template <class T>
void Interval<T>::set_max(const T& max) {
	max_defined_ = true;
	max_ = max;
}

//! Set the lower bound of the interval to -infinity
template <class T>
void Interval<T>::remove_min() {
	min_defined_ = false;
}

//! Set the higher bound of the interval to -infinity
template <class T>
void Interval<T>::remove_max() {
	max_defined_ = false;
}

//! Check if the lower bound is included in the interval
/*!
 @return True if lower bound is included in the interval, false otherwise
 */
template <class T>
bool Interval<T>::min_included() const {
	return min_included_;
}

//! Check if the higher bound is included in the interval
/*!
 @return True if higher bound is included in the interval, false otherwise
 */
template <class T>
bool Interval<T>::max_included() const {
	return max_included_;
}

//! Check if the lower bound is included in the interval
/*!
 @return True if lower bound is included in the interval, false otherwise
 */
template <class T>
bool& Interval<T>::min_included() {
	return min_included_;
}

//! Check if the higher bound is included in the interval
/*!
 @return True if higher bound is included in the interval, false otherwise
 */
template <class T>
bool& Interval<T>::max_included() {
	return max_included_;
}

//! Check if the interval is empty
/*!
 @return True if the interval is empty, false otherwise
 @note If the interval contains one single value (i.e., that the lower and
       higher bound have the same value and are both included in the
	   interval), it is considered empty
 */
template <class T>
bool Interval<T>::empty_interval() const {
	if ( min_defined_ == false ) return false;
	if ( max_defined_ == false ) return false;
	return ( min_ >= max_ );
}

//! Check if the interval is full
/*!
 @return True if the interval is full, false otherwise
 @note The interval is full if both the lower and higher bounds are set to
	   +/- infinity
 */
template <class T>
bool Interval<T>::full_interval() const {
	if ( ( min_defined_ == false ) && ( max_defined_ == false ) ) return true;
	return false;
}

//! Check if the interval is finite, i.e., that both bounds are defined
/*!
 @return True if the interval is finite, false otherwise
 */
template <class T>
bool Interval<T>::finite_interval() const {
	return ( ( min_defined_ == true ) && ( max_defined_ == true ) );
}

//! Check if given value falls within the interval
/*!
 @param value The value to test
 @return True if value is in the interval, false otherwise
 */
template <class T>
bool Interval<T>::inside(const T& value) const {
	// Check lower bound
	if ( min_defined_ ) {
		if ( min_included_ ) {
			if ( value < min_ ) return false;
		}
		else {
			if ( value <= min_ ) return false;
		}
	}

	// Check higher bound
	if ( max_defined_ ) {
		if ( max_included_ ) {
			if ( value > max_ ) return false;
		}
		else {
			if ( value >= max_ ) return false;
		}
	}

	// If both bounds are OK, value is in interval
	return true;
}

//! Check if both intervals intersect
/*!
 @param rhs The other interval to test for intersection
 @return True if both intervals have a non-empty intersection, false otherwise
 */
template <class T>
bool Interval<T>::intersect(const Interval& rhs) const {
	return ( intersection(rhs).empty() == false );
}

//! Check if interval rhs is included in this interval
/*!
 @param rhs The interval to test for inclusion
 @return True if interval rhs is included in this interval, false otherwise
 */
template <class T>
bool Interval<T>::included(const Interval& rhs) const {
	return ( intersection(rhs) == rhs );
}

//! Get the intersection of both intervals
/*!
 @param rhs The other interval with which to perform intersection
 @return The intersection of both intervals
 */
template <class T>
Interval<T> Interval<T>::intersection(const Interval& rhs) const {
	Interval<T> res;

	// Set lower bound
	res.set_min( min_ > rhs.min_ ? min_ : rhs.min_ );
	if ( ( min_defined_ == false ) && ( rhs.min_defined_ == false ) ) res.remove_min();
	res.min_included() = ( min_ > rhs.min_ ? min_included_ : rhs.min_included_ );
	if ( min_ == rhs.min_ ) res.min_included() = ( min_included_ && rhs.min_included_ );

	// Set higher bound
	res.set_max( max_ < rhs.max_ ? max_ : rhs.max_ );
	if ( ( max_defined_ == false ) && ( rhs.max_defined_ == false ) ) res.remove_max();
	res.max_included() = ( max_ < rhs.max_ ? max_included_ : rhs.max_included_ );
	if ( max_ == rhs.max_ ) res.max_included() = ( max_included_ && rhs.max_included_ );

	// Return the intersection interval
	return res;
}

//! Get the length of the interval
/*!
 @return The length of the interval, i.e., abs(max-min)
 @note Return -1 if either max or min bounds are infinite
 */
template <class T>
T Interval<T>::length() const {
	// Check for infinite bounds
	if ( min_defined_ == false ) return (T)-1;
	if ( max_defined_ == false ) return (T)-1;

	// Compute result
	T res = max_-min_;

	// Return absolute value
	if ( res < 0 ) return -res;
	return res;
}

//=================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
